package quick.start.core.async;

import quick.start.core.R;
import quick.start.core.exception.CoreException;
import quick.start.core.exception.NullDataException;
import quick.start.core.exception.OperationFailException;
import quick.start.core.exception.SysException;
import quick.start.core.extend.ToastExtend;
import quick.start.core.util.CoreLog;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;


/**
 * 对取数过程中的loading, error, empty, normal进行管理并操作相应的数据容器控件</br> 目前各种状态的判断依赖于
 * {@link com.up91.common.android.exception}下的异常类型
 */
public abstract class DefaultLoader<D> extends AsyncTaskLoader<D> implements OnClickListener {

	private FrameLayout areaRoot;
	private FrameLayout areaState;
	private View viewNormalState;
	private View viewLoadingState;
	private View viewErrorState;
	private View viewEmptyState;
	
	private TextView txtErrorMsg;
	private TextView txtEmptyMsg;
	private Button btnRetry;
	private Exception exception;
	
	private D data;
	
	private enum State{
		Loading, Error, Empty, Normal
	}
	
	public DefaultLoader(Context context, View view){
		super(context);
		this.viewNormalState = view;
		initView();
	}
	
	protected abstract D loadData() throws Exception;
	
	protected void handleException(){
        try {
            throw exception;
        } catch (SysException e) {
            changeState(e.isRecoverable() ? State.Error : State.Empty);
            TextView tip = e.isRecoverable() ? txtErrorMsg : txtEmptyMsg;
            tip.setText(e.getMessage());
        } catch (NullDataException e) {
            changeState(State.Empty);
            txtEmptyMsg.setText(exception.getMessage());
        } catch (OperationFailException e) {
            changeState(State.Error);
            txtErrorMsg.setText(exception.getMessage());
        } catch (CoreException e) {
            changeState(e.isRecoverable() ? State.Error : State.Empty);
            TextView tip = e.isRecoverable() ? txtErrorMsg : txtEmptyMsg;
            tip.setText(e.getMessage());
        } catch (Exception e) {
            ToastExtend.instance(getContext()).setText(exception.getMessage()).show();
        } finally {
            CoreLog.i(this.getClass(), exception.getMessage() + "");
            exception = null;
        }
	}
	
	private void initView(){
		if(null == viewNormalState){
			throw new IllegalArgumentException("data container can't null");
		}
		ViewGroup parent = (ViewGroup)viewNormalState.getParent();
		if (!(parent instanceof FrameLayout)){
			throw new IllegalStateException("the parent of data container must be framelayout");
		}
		
		FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)viewNormalState.getLayoutParams();
		areaRoot = (FrameLayout)LayoutInflater.from(getContext()).inflate(R.layout.loader, null);
		parent.addView(areaRoot, params);
		
        areaState = (FrameLayout) areaRoot.findViewById(R.id.loader_state);
        areaState.setLayoutParams(params);
        viewLoadingState = areaRoot.findViewById(R.id.loader_loading);
        viewErrorState = areaRoot.findViewById(R.id.loader_error);
        txtErrorMsg = (TextView) areaRoot.findViewById(R.id.loader_tv_errormsg);
        btnRetry = (Button) areaRoot.findViewById(R.id.loader_btn_retry);
        btnRetry.setOnClickListener(this);
        viewEmptyState = areaRoot.findViewById(R.id.loader_empty);
        txtEmptyMsg = (TextView) areaRoot.findViewById(R.id.loader_tv_empty);
		
	}
	
	private void makeViewVisible(View v){
		for (int i = 0; i < areaState.getChildCount(); i++) {
			View child = areaState.getChildAt(i);
			if(child != v){
				child.setVisibility(View.INVISIBLE);
			}else{
				child.setVisibility(View.VISIBLE);
			}
		}
	}
	
	private void changeState(State state){
		if(state.Normal == state){
			areaState.setVisibility(View.INVISIBLE);
			viewNormalState.setVisibility(View.VISIBLE);
		}else{
			areaState.setVisibility(View.VISIBLE);
			viewNormalState.setVisibility(View.INVISIBLE);
			
			switch (state) {
			case Error:
				makeViewVisible(viewErrorState);
				break;
			case Empty:
				makeViewVisible(viewEmptyState);
				break;
			case Loading:
				makeViewVisible(viewLoadingState);
				break;
			}
		}
	}

	
	@Override
	public void onClick(View v) {
		if(v.getId() == btnRetry.getId()){
			onStartLoading();
		}
	}

	@Override
	public D loadInBackground() {
		D result = null;
		try{
			result = loadData();
			CoreLog.i(this.getClass(), "result=" + result);
		}catch(Exception e){
			e.printStackTrace();
			this.exception = e;
		}
		return result;
	}

	@Override
	public void deliverResult(D data) {
		changeState(State.Normal);
		if(null != exception){
			handleException();
		}
		this.data = data;
		super.deliverResult(data);
	}

	@Override
	protected void onAbandon() {
		if(null != viewNormalState){
			ViewGroup parent = (ViewGroup)areaRoot.getParent();
			parent.removeView(areaRoot);
		}
		super.onAbandon();
	}

	@Override
	protected void onStartLoading() {
        if (null != this.data) {
            deliverResult(this.data);
            return;
        }
        reset();
        changeState(State.Loading);
        forceLoad();
	}

	@Override
	public void reset() {
		cancelLoad();
		super.deliverResult(null);
		this.data = null;
		this.exception = null;
		changeState(State.Normal);
		super.reset();
	}
	
	
}
